#ifndef P2P_BASE_PORTALLOCATOR_H_
#define P2P_BASE_PORTALLOCATOR_H_

#include <string>
#include <vector>

#include "base/helpers.h"
#include "base/sigslot.h"
#include "portinterface.h"
#include "base/proxyinfo.h"

namespace cricket
{
	// PortAllocator is responsible for allocating Port types for a given
	// P2PSocket. It also handles port freeing.
	//
	// Clients can override this class to control port allocation, including
	// what kinds of ports are allocated.

	const uint32 PORTALLOCATOR_DISABLE_UDP = 0x01;
	const uint32 PORTALLOCATOR_DISABLE_STUN = 0x02;
	const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04;
	const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08;
	const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10;
	const uint32 PORTALLOCATOR_ENABLE_BUNDLE = 0x20;
	const uint32 PORTALLOCATOR_ENABLE_IPV6 = 0x40;
	const uint32 PORTALLOCATOR_ENABLE_SHARED_UFLAG = 0x80;
	const uint32 PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100;
	const uint32 PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200;
	const uint32 PORTALLOCATOR_USE_LARGE_SOCKET_SEND_BUFFERS = 0x400;

	const uint32 kDefaultPortAllocatorFlags = 0;

	class PortAllocatorSessionMuxer;

	class PortAllocatorSession : public sigslot::has_slots<>
	{
	public:
		PortAllocatorSession(const std::string& content_name,
			int component, const std::string& username,
			const std::string& password, uint32 flags);
		// Subclasses should clean up any ports created.
		virtual ~PortAllocatorSession() {}

		uint32 flags() const { return flags_; }
		void set_flags(uint32 flags) { flags_ = flags; }
		std::string content_name() const { return content_name_; }
		int component() const { return component_; }

		// Prepares an initial set of ports to try.
		virtual void GetInitialPorts() = 0;

		// Starts and stops the flow of additional ports to try.
		virtual void StartGetAllPorts() = 0;
		virtual void StopGetAllPorts() = 0;
		virtual bool IsGettingAllPorts() = 0;

		sigslot::signal2<PortAllocatorSession*, PortInterface*> SignalPortReady;
		sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&> SignalCandidatesReady;
		sigslot::signal1<PortAllocatorSession*> SignalCandidatesAllocationDone;

		virtual uint32 generation() { return generation_; }
		virtual void set_generation(uint32 generation) { generation_ = generation; }
		sigslot::signal1<PortAllocatorSession*> SignalDestroyed;

	protected:
		const std::string& username() const { return username_; }
		const std::string& password() const { return password_; }

		std::string content_name_;
		int component_;

	private:
		uint32 flags_;
		uint32 generation_;
		std::string username_;
		std::string password_;
	};

	class PortAllocator : public sigslot::has_slots<>
	{
	public:
		PortAllocator() : flags_(kDefaultPortAllocatorFlags), min_port_(0), max_port_(0)
		{
		}
		virtual ~PortAllocator();

		PortAllocatorSession* CreateSession(
			const std::string& sid,
			const std::string& content_name,
			int component,
			const std::string& ice_ufrag,
			const std::string& ice_pwd);

		PortAllocatorSessionMuxer* GetSessionMuxer(const std::string& key) const;
		void OnSessionMuxerDestroyed(PortAllocatorSessionMuxer* session);

		uint32 flags() const { return flags_; }
		void set_flags(uint32 flags) { flags_ = flags; }

		const std::string& user_agent() const { return agent_; }
		const base::ProxyInfo& proxy() const { return proxy_; }
		void set_proxy(const std::string& agent, const base ProxyInfo& proxy)
		{
			agent_ = agent;
			proxy_ = proxy;
		}

		// Gets/Sets the port range to use when choosing client ports.
		int min_port() const { return min_port_; }
		int max_port() const { return max_port_; }
		bool SetPortRange(int min_port, int max_port)
		{
			if (min_port > max_port)
				return false;

			min_port_ = min_port;
			max_port_ = max_port;
			return true;
		}

	protected:
		virtual PortAllocatorSession* CreateSessionInternal(
			const std::string& content_name,
			int component,
			const std::string& ice_ufrag,
			const std::string& ice_pwd) = 0;

		typedef std::map<std::string, PortAllocatorSessionMuxer*> SessionMuxerMap;

		uint32 flags_;
		std::string agent_;
		base::ProxyInfo proxy_;
		int min_port_;
		int max_port_;
		SessionMuxerMap muxers_;
	};
}

#endif	// P2P_BASE_PORTALLOCATOR_H_